#include <iostream>
#include <sistema.h>
#include <auxiliares.h>
#include <cmath>

/*Inicializa el sistema de ecuaciones con la matriz cuadrada nula de nxn y con b igual al vector nulo*/
sistema::sistema(unsigned int n):resuelto(false), x(n, 0.0), A(n,n, 0.0), b(n, 0.0){}
       
/*Inicializa el sistema a partir de la matriz A y el vector b
 * IMPORTANTE: el vector b y la matriz A deben coincidir en la cantidad de filas!!!
 */
sistema::sistema(matriz<double>& M, vector<double>& c): resuelto(false), x(c.size(), 0.0), A(M), b(c) {}

/*Inicializa la matriz asociada al sistema*/
void sistema::setMatriz(matriz<double>& A){
    this->A = A;
}

/*Inicializa la i-esima fila de la matriz asociada al sistema*/
void sistema::setFila(vector<double>& terminos, unsigned int i){
    if(i < A.cant_filas()){
        A[i] = terminos;
    }
}

/*Inicializa el j-esimo termino del a i-esima ecuacion*/
void sistema::setTermino(double a, unsigned int i, unsigned int j){
    if(i < A.cant_filas() && j < A.cant_columnas()){
        A[i][j] = a;
    }
}

double sistema::getTermino(unsigned int i, unsigned int j){
    if(i < A.cant_filas() && j < A.cant_columnas()){
        return A[i][j];
    }
    else{
        return 0.0;
    }
}

/*Inicializa el vector de terminos independientes*/
void sistema::setTerminosIndependientes(vector<double>& b){
    this->b = b;
}

/*Inicializa el i-esimo termino independiente*/
void sistema::setTerminoIndependiente(double b, unsigned int i){
    if(i < (this->b).size()){
        this->b[i] = b;
    }
}

/*Resuelve el sistema*/
void sistema::resolver(){
   
   		 
    if(!triangular()){
       
        for(unsigned int i=A.cant_filas(); i>0; i--){
            double temp = 0;
           
            for(unsigned int k=A.cant_columnas(); k>i; k--){
                temp += x[k-1] * A[i-1][k-1];
            }
            x[i-1] = (b[i-1] - temp)/A[i-1][i-1];
        }
    }
}

/*Devuelve el valor de la i-esima incognita*/
double sistema::getIncognita(unsigned int i){
    if(!resuelto){
        resolver();
    }
   
    return x[i];
}

bool sistema::esFilaNula(matriz<double>& M, int fila)
{
	for(unsigned int i = 0; i< M.cant_columnas(); i++)
		if(!esCero(M[fila][i]))
			return  false;

   return true;
}


bool sistema::hayFilaNula(){
   for(unsigned int i = 0; i< A.cant_filas(); i++){
           if(esFilaNula(A,i))
           {
                   cout<< " hay una fila nula no podes triangular"<<endl;
                   return  true;
           }
   }
   return false;
}



bool sistema::triangular(){
    for (unsigned int j=0; j < A.cant_columnas(); j++) {
        pivoteo(j,j);
        for (unsigned int i=j+1; i<A.cant_filas(); i++) {
            float coeficiente = (A[i][j])/(A[j][j]);
            restaAFila(j,i, coeficiente);
            //Asumo que queda un 0 en la posicion i,j (ya que podria, por errores de redondeo, no quedar 0)
            A[i][j] = 0;
        }
    }
    return (hayFilaNula());
}

/** Intercambia de lugar dos filas */
void sistema::swapFila(int f1, int f2){
    double btemp = b[f1];
    vector<double> ftemp = A[f1];
    A[f1] = A[f2];
    b[f1] = b[f2];
    A[f2] = ftemp;
    b[f2] = btemp;
}

/**
* Busca el elemento de mayor módulo de la columna
* a partir de la fila indicada hasta el final, y
* swapea las filas.
*/
void sistema::pivoteo(int fila, int columna){
    int filaACambiar = fila;
    for (unsigned int i=fila; i<A.cant_filas(); i++) {
        if (abs(A[i][columna]) > abs(A[filaACambiar][columna]) ) {
            filaACambiar = i;
        }
    }
    swapFila(fila, filaACambiar);
}

/**
* Resta a fila2 un múltiplo de fila1.
*/
void sistema::restaAFila(int fila1, int fila2, double coeficiente){
    for(unsigned int j=0; j < A.cant_columnas(); j++) {
      //  A[fila2][j] -= coeficiente * A[fila1][j];
      A[fila2][j] =A[fila2][j] - ( coeficiente * A[fila1][j]);
    }
    //Actualizo el termino independiente
    b[fila2] -= coeficiente * b[fila1];
}

/** Muestra la matriz mediante el ostream pasado */
void sistema::mostrar(std::ostream& out) {
    for(unsigned int i=0; i < A.cant_filas(); i++){
        out << "( ";        
        for(unsigned int j=0; j<A.cant_columnas(); j++) {
            out << A[i][j] << " ";
        }
        out << "| " << b[i];
        out << " )" << endl;
    }
}
